node.js — koa — Typescript 微信连接api

node.js — koa — Typescript 微信连接api

服务器中间层框架为koa,使用node原生的加密方式,应该整个公司项目的微信api都从一个接口上出去,不应该分散到各个项目去单独调api,便于管理维护,减少调用次数

memory-cache为请求到的access_token做缓存,微信调用access_token每日有次数限制。目前是根据时间判断的,理应后期加上定时任务

  1. 获取access_token存入缓存,有效期两小时,有的话从缓存中取
  2. 获取ticket存入缓存,有效期两小时,有的话从缓存中取
  3. 将获取到的ticket和其他三个参数进行sha1-HEX加密,拿到签名signature
  4. 提供一个接口供前端调(ps: 敏感字段请勿传给前台)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import * as koa from "koa";
import { HTTPProtocol } from "@Constance/interface"

import { getData, httpType } from '@Utils/http'
import Config from '@Config/config'
import * as cache from 'memory-cache'
const crypto = require('crypto');

interface IAccessTokenResponse {
errcode: number;
errmsg: string;
access_token: string;
expires_in: number;
}
interface jsAPITicketResponse {
errcode: number;
errmsg: string;
ticket: string;
expires_in: number;
}

const getWxConfig = async (ctx: koa.Context) => {


const { url } = ctx.query

// 返回给前台的数据
let res: HTTPProtocol<any> = {
code: 0,
msg: "成功",
data: ""
}

if (!url) {
res.msg = "无URL"
ctx.body = res
return;
}



let timestamp: number = Math.floor(Date.now() / 1000);

// 获取access_token
if (!cache.get("tokenObj") || cache.get("tokenObj").expires_in < timestamp || cache.get("tokenObj").app_id !== Config.wx_gzh.app_id) {

let getAccessURL = Config.wx_gzh.access_token + `&appid=` + Config.wx_gzh.app_id + `&secret=` + Config.wx_gzh.app_secret

const WxAccessToken = await getData<IAccessTokenResponse>(getAccessURL, httpType.get)

if (WxAccessToken && WxAccessToken.access_token) {
cache.put("tokenObj", {
access_token: WxAccessToken.access_token,
expires_in: timestamp + (WxAccessToken.expires_in - 1800),
app_id: Config.wx_gzh.app_id
}, 1.5 * 60 * 60 * 1000)
} else {
cache.put("tokenObj", null);
ctx.logger.error(new Error(`wxconfig: ${JSON.stringify(Config.wx_gzh)}`));
ctx.logger.error(new Error(`tokenResult: ${JSON.stringify(WxAccessToken)}`));
}
}

res.code = 500
res.msg = "获取access失败"


// 获取ticket
if (cache.get("tokenObj") && cache.get("tokenObj").app_id === Config.wx_gzh.app_id) {
timestamp = Math.floor(Date.now() / 1000);
if (!cache.get("jsapiObj") || cache.get("jsapiObj").expires_in < timestamp || cache.get("jsapiObj").app_id !== Config.wx_gzh.app_id) {

let getTicketURL = Config.wx_gzh.js_api + `&access_token=` + cache.get("tokenObj").access_token
const WxTicket = await getData<jsAPITicketResponse>(getTicketURL, httpType.get)

if (WxTicket && WxTicket.ticket) {
cache.put("jsapiObj", {
ticket: WxTicket.ticket,
expires_in: timestamp + (WxTicket.expires_in - 1800),
app_id: Config.wx_gzh.app_id
}, 1.5 * 60 * 60 * 1000)
} else {
cache.put("jsapiObj", null);
ctx.logger.error(new Error(`wxconfig: ${JSON.stringify(Config.wx_gzh)}`));
ctx.logger.error(new Error(`jsapiResult: ${JSON.stringify(WxTicket)}`));
}
}
if (cache.get("jsapiObj") && cache.get("jsapiObj").app_id === Config.wx_gzh.app_id) {

const jsapi_ticket = cache.get("jsapiObj").ticket;
const nonceStr = Config.wx_gzh.nonce_str
timestamp = Math.floor(Date.now() / 1000);
const string1 = `jsapi_ticket=${jsapi_ticket}&noncestr=${nonceStr}&timestamp=${timestamp}&url=${url}`;
const signature = sha1Trans(string1)
res.code = 0
res.msg = "成功"
res.data = {
nonceStr: nonceStr,
timestamp: timestamp,
signature: signature,
appId: Config.wx_gzh.app_id
};
}
}

function sha1Trans(str: string) {
var sha1 = crypto.createHash('sha1');//创建哈希加密算法,后边可以是md5,sha1,sha256等
var newStr = sha1.update(str).digest('HEX');
return newStr
}

ctx.body = res

};

export {
getWxConfig
}

配置文件为:

1
2
3
4
5
6
7
"wx_gzh": {
"nonce_str": "随机字符串",
"app_id": "你自己的",
"app_secret": "你自己的",
"js_api": "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi",
"access_token": "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"
}
感谢你的打赏哦!